#include "Camera.h"
#include "Core/Math/MatrixMath.h"

namespace SL
{
float Camera::focalLengthToFovY(float focalLength, float frameHeight) const
{
    return 2.0f * std::atan(0.5f * frameHeight / focalLength);
}

void Camera::calculateParameters() const
{
    if (bDirty)
    {
        cameraData.viewMat = math::matrixLookAt(cameraData.posW, cameraData.target, cameraData.up);

        const float fovY = cameraData.focalLength == 0.0f ? 0.0f : focalLengthToFovY(cameraData.focalLength, cameraData.frameHeight);
        // TODO: add jittering support
        cameraData.projMat = math::perspectiveFov(fovY, cameraData.aspectRatio, cameraData.nearZ, cameraData.farZ);
        cameraData.viewProjMat = cameraData.projMat * cameraData.viewMat;
        
        cameraData.invViewProj = cameraData.viewProjMat.inverse();

        cameraData.viewProjMatNoJitter = cameraData.viewProjMat;
        cameraData.projMatNoJitter = cameraData.projMat;

        bDirty = false;
    }
}

float* Camera::getPosVec()
{
    setDirty();
    return cameraData.posW.data();
}

void Camera::setDirty()
{
    bDirty = true;
}

void Camera::setNearZ(const float& z)
{
    cameraData.nearZ = z;
    bDirty = true;
}

void Camera::setFarZ(const float& z)
{
    cameraData.farZ = z;
    bDirty = true;
}

void Camera::setPosition(const float3& position)
{
    cameraData.posW = position;
    bDirty = true;
}
void Camera::setTarget(const float3& target)
{
    cameraData.target = target;
    bDirty = true;
}
void Camera::setUpVector(const float3& up)
{
    cameraData.up = up;
    bDirty = true;
}
const float4x4 Camera::getViewMatrix() const
{
    calculateParameters();
    return cameraData.viewMat;
}
const float4x4 Camera::getPrevViewMatrix() const
{
    calculateParameters();
    return cameraData.prevViewMat;
}
const float4x4 Camera::getProjMatrix() const
{
    calculateParameters();
    return cameraData.projMat;
}
const float4x4 Camera::getViewProjMatrix() const
{
    calculateParameters();
    return cameraData.viewProjMat;
}
const float4x4 Camera::getViewProjMatrixNoJitter() const
{
    calculateParameters();
    return cameraData.viewProjMatNoJitter;
}

const float4x4 Camera::getInvViewProjMatrix() const
{
    calculateParameters();
    return cameraData.invViewProj;
}

const float3 Camera::getUpVec() const
{
    return cameraData.up;
}

const float3 Camera::getPosition() const
{
    return cameraData.posW;
}

const float Camera::getAspectRatio() const
{
    return cameraData.aspectRatio;
}
} // namespace SL